﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace DmxInterface {

	/// <summary>Represents a DMX device and issues commands to it.</summary>
	public class Device : IDisposable {

		#region Creation

		int _id;

        private Device() {
        }

		internal Device(int id) {
			_id = id;
		}

		#endregion

		#region Status

		bool _isOpen = false;

		/// <summary>Gets whether the device is currently open or not.</summary>
		public bool IsOpen {
			get {
				return _isOpen;
			}
		}

		ErrorResilience _resilience = ErrorResilience.IgnoreCommunicationErrors;

		/// <summary>Gets or sets the error resilience level of the device.</summary>
		public ErrorResilience ErrorResilience {
			get {
				return _resilience;
			}
			set {
				_resilience = value;
			}
		}

		#endregion

		#region Interfacing methods

		/// <summary>Attempts to open the device. Will throw if unable to do so.</summary>
		public void Open() {
			if (_isOpen)
				return;

			if (NativeMethods.IssueDmxCommand(NativeMethods.Command.Open, _id) != (int)NativeMethods.ReturnCode.Ok)
				throw new Exception(Strings.ErrOpenDevice);
			
			_isOpen = true;
		}

		/// <summary>Attempts to close the device. Will throw if unable to do so.</summary>
		public void Close() {
			if (!_isOpen)
				return;

			int ret = NativeMethods.IssueDmxCommand(NativeMethods.Command.Close, _id);

			if (ret == (int)NativeMethods.ReturnCode.Ok) {
				_isOpen = false;
				return;
			}
			else if (ret == (int)NativeMethods.ReturnCode.NotOpenError)
				throw new Exception(Strings.ErrNotOpen);
			else if (ret == (int)NativeMethods.ReturnCode.CommandError)
				throw new Exception(Strings.ErrCloseDevice);
			else
				throw new Exception(Strings.ErrUnexpected);
		}

		/// <summary>Resets the state of the device, settings 0 values on all output channels.</summary>
		public void ResetOutput() {
			if (!_isOpen)
				throw new InvalidOperationException(Strings.DeviceMustBeOpen);

			int ret = NativeMethods.IssueDmxCommand(NativeMethods.Command.DmxOutOff, _id);

			if (ret == (int)NativeMethods.ReturnCode.Ok)
				return;
			else if (ret == (int)NativeMethods.ReturnCode.NotOpenError)
				throw new Exception(Strings.ErrNotOpen);
			else if (ret == (int)NativeMethods.ReturnCode.CommandError)
				throw new Exception(Strings.ErrReset);
			else
				throw new Exception(Strings.ErrUnexpected);
		}

		/// <summary>Sends a data block on the out channel.</summary>
		/// <param name="data">DMX data as block of byte values.</param>
		public void SendData(byte[] data) {
			if (!_isOpen)
				throw new InvalidOperationException(Strings.DeviceMustBeOpen);

			int ret = NativeMethods.IssueDmxCommand(NativeMethods.Command.DmxOut, _id,  data);

			if (ret == (int)NativeMethods.ReturnCode.NotOpenError)
				throw new Exception(Strings.ErrNotOpen);
			else if (_resilience == ErrorResilience.SignalAllErrors) {
				if (ret == (int)NativeMethods.ReturnCode.CommandError)
					throw new Exception(Strings.ErrSend);
				else
					throw new Exception(Strings.ErrUnexpected);
			}
		}

		#endregion

		#region IDisposable Members

		public void Dispose() {
			Close();
		}

		#endregion
	}
}
